package com.comp.code.generator.genoper;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.comp.code.generator.model.GenInfo;
import com.comp.code.generator.util.FileUtil;
import com.comp.code.generator.util.FreeMarkUtil;
import com.comp.code.generator.util.PropertyUtil;
import com.comp.code.generator.util.StringUtil;

/**
 * 代码生成处理器
 * @author HeJian
 *
 */
public class GenerateHandler {
	/** 生成物理目录 **/
	private String path="";
	/** 微服务应用名称 **/
	private String applicationName="";
	
	/** 包前缀 **/
	private String packagePre = "";
	/** 子模块包 **/
	private String subPackage="";
	/** api完整包名称 **/
	private String packageApi="";
	/** controller完整包名称 **/
	private String packageController="";
	/** facade完整包名称 **/
	private String packageFacade="";
	/** mapper接口完整包名称 **/
	private String packageMapper="";
	/** model完整包名称 **/
	private String packageModel="";
	/** service接口完整包名称 **/
	private String packageService="";
	/** service实现完整包名称 **/
	private String packageServiceImpl="";
	
	/** 表名称 **/
	private String table="";
	/** 表主键,多个主键用,号隔开 **/
	private String pks="";
	/** 所有的列名称 **/
    private List<String> colNames = new ArrayList<String>();
    /** 表字段类型 **/
	private List<String> colTypes = new ArrayList<String>();
	/** 表字段的宽度 **/
    private List<Integer> colSizes = new ArrayList<Integer>();
    /** 表字段是否允许为 NULL **/
    private List<Integer> colNullables = new ArrayList<Integer>();
    /** 表字段注释 **/
    private List<String> colRemarks = new ArrayList<String>();
    /** 表注释 **/
    private String tableRemark="";
	
	/** model类名 **/
	private String model="";
	/** api类名 **/
	private String api="";
	/** controller类名 **/
	private String controller="";
	/** facade类名 **/
	private String facade="";
	/** mapper类名 **/
	private String mapper="";
	/** service类名 **/
	private String service="";
	/** service实现类名 **/
	private String serviceImpl="";
	
	/** 自增主键: key 属性名 value: YES **/
    private Map<String,String> idAutoIncrement = new HashMap<String,String>();
    /**主键的java类型  key为主键名称转java驼峰的名称, value为java类型**/
  	private Map<String,String> pkJavaTyps = new HashMap<String,String>();
	
	
	/*
	 * 生成代码
	 * @param genInfo
	 */
	public void generate(GenInfo genInfo) {
		try {
			System.out.println("===>>>开始生成代码!");
			//数据项校验
			if(genInfo==null) { throw new RuntimeException("代码生成参数为空!"); }
			String applicationName = genInfo.getApplicationName();
			String[] packageNames = genInfo.getPackageNames();
			String packagePre = genInfo.getPackagePre();
			String path = genInfo.getPath();
			String[] tables = genInfo.getTables();
			String subPackage = genInfo.getSubPackage();
			if(tables==null || tables.length==0){
				throw new RuntimeException("<<<===请指定数据库表的参数!");
			}
			if(packageNames==null || packageNames.length==0){
				throw new RuntimeException("<<<===请指定要生成的包!");
			}
			if(packagePre==null || packagePre.trim().length()==0){
				throw new RuntimeException("<<<===请指定包前缀!");
			}
			if(path==null || path.trim().length()==0){
				throw new RuntimeException("<<<===请指定代码生成的绝对路径!");
			}
			if(applicationName==null || applicationName.trim().length()==0){
				throw new RuntimeException("<<<===请指定微服务应用名称!");
			}
			if(subPackage==null || subPackage.trim().length()==0){
				subPackage = "";
			}
			
			//循环为每个表生成代码
			for(String table:tables){
				if(table==null || table.trim().length()==0) continue;
				//生成代码
				new GenerateHandler().genHandle(path,packagePre,subPackage,applicationName,packageNames,table);
			}
			
			System.out.println("<<<===代码生成完成!");
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println(e.getMessage());
		}
	}

	/**
	 * 单表处理生成代码
	 * @param path 代码生成的路径
	 * @param packagePre	包前缀
	 * @param applicationName	微服务应用名称
	 * @param packageNames	要生成的代码组件
	 * @param table	生成的表
	 */
	private void genHandle(String path, String packagePre, String subPackage,
			String applicationName, String[] packageNames, String table) {
		//基本信息赋值转换;
		this.subPackage=subPackage.trim();
		this.packagePre = packagePre=packagePre.trim() + ((this.subPackage==null || this.subPackage.trim().length()==0 )? "":"."+this.subPackage);
		this.table = table.trim().toUpperCase();
		this.path=path.trim();
		this.model = GenerateHelper.getEntityName(this.table).trim(); 
		for(String pack : packageNames) {
			pack = pack.trim();
			if("model".equals(pack)){
				this.packageModel = packagePre+"."+pack;
			}
			if("mapper".equals(pack)){
				this.packageMapper = packagePre+"."+pack;
				this.mapper=this.model+"Mapper";
			}
			if("service".equals(pack)){
				this.packageService =  packagePre+"."+pack;
				this.packageServiceImpl = this.packageService+".impl";
				this.service="I"+this.model+"Service";
				this.serviceImpl=this.model+"ServiceImpl";
			}
			if("api".equals(pack)){
				//判断应用名称是否不为空
				if(applicationName==null || applicationName.trim().length()==0){
					throw new RuntimeException("<<<===applicationName参数为空:当要生成api接口层此参数为必须!");
				}
				this.applicationName = applicationName.trim();
				this.packageApi = packagePre+"."+pack;
				this.packageFacade = packagePre+".facade";
				this.api="I"+this.model+"Api";
				this.facade="I"+this.model+"Facade";
			}
			if("controller".equals(pack)){
				this.packageController = packagePre+"."+pack;
				this.controller=this.model+"Controller";
			}
		}
		
		//连接数据库
		String driverClass=PropertyUtil.getProperty("jdbc.driver");
		String url=PropertyUtil.getProperty("jdbc.url");
		String username=PropertyUtil.getProperty("jdbc.username");
		String password=PropertyUtil.getProperty("jdbc.password");
		
		//连接数据库
		Connection con = null;
		String sql = "SELECT * FROM " + this.table + " LIMIT 1";
		Statement pStemt = null;
		try {
			Class.forName(driverClass);
			
			Properties props =new Properties();
			props.put("user",username);
			props.put("password",password );
			props.put("remarksReporting","true");
			con = DriverManager.getConnection(url, props);
			
			pStemt = (Statement) con.createStatement();
			ResultSet rs = pStemt.executeQuery(sql);
			ResultSetMetaData rsmd = rs.getMetaData();
			DatabaseMetaData metaData = con.getMetaData();
			ResultSet r = metaData.getPrimaryKeys(null, "", table);
			//获取主键信息
			while (r.next()) {
				String columnName = r.getString("COLUMN_NAME");
				pks = pks + columnName + ",";
			}
			if(pks.trim().length()==0){
				System.err.println("<<<===警告:该表没有主键!");
			}else{
				pks = pks.substring(0,pks.length()-1);
			}
			ResultSet colRet = metaData.getColumns(null,"%", this.table,"%"); 
			while(colRet.next()){
				String columnName = colRet.getString("COLUMN_NAME");
				//是否是自增
				String isAutoincrement = colRet.getString("IS_AUTOINCREMENT");
				if("YES".equalsIgnoreCase(isAutoincrement)){
					//如果自增主键,给个自增标记
					idAutoIncrement.put(columnName.trim(), "YES");//GenerateHelper.getAttrName(columnName)
				}
			}
			
			//获取表中列信息
			int size = rsmd.getColumnCount(); //列数目
			for (int i = 0; i < size; i++) {
				//列名
				colNames.add(rsmd.getColumnName(i + 1));
				//字段类型
				colTypes.add(rsmd.getColumnTypeName(i + 1));
				//字段允许的宽度
				colSizes.add( rsmd.getColumnDisplaySize(i + 1) );
				//字段是否允许为NULL
				colNullables.add(rsmd.isNullable(i + 1));
				//获取主键类型
				if(pks.indexOf(colNames.get(i))!=-1){
					String attKey = GenerateHelper.getAttrName(colNames.get(i)); //属性名称
					String typeValue = GenerateHelper.sqlType2JavaType(colTypes.get(i));	//java类型
					this.pkJavaTyps.put(attKey, typeValue);
				}
			}
			//列字段的注释
			ResultSet rr = con.getMetaData().getColumns(null, "%", this.table, "%");
			while(rr.next()){
				colRemarks.add(rr.getString("REMARKS"));
				//System.out.println("字段名："+rr.getString("COLUMN_NAME")+"\t字段注释："+rr.getString("REMARKS")+"\t字段数据类型："+rr.getString("TYPE_NAME"));
			}
			
			//表名的注释
			ResultSet trs = pStemt.executeQuery("SHOW CREATE TABLE " + this.table);
            if (trs != null && trs.next()) {
                String create = trs.getString(2);
                this.tableRemark=GenerateHelper.getTableCommet(create);
            }
			
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		} finally {
			try {
				con.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		//开始代码生成
		doGenerate();
	}

	/**
	 * 根据获取到的原数据生成相应代码
	 */
	private void doGenerate() {
		//生成model
		if(!StringUtil.isEmpty(this.packageModel)){
			this.generateModel();
		}
		//生成mapper 和mapper对应的xml
		if(!StringUtil.isEmpty(this.packageMapper) ){
			this.generateMapper();
		}
		//生成service 和 service实现类
		if(!StringUtil.isEmpty(this.packageService)){
			this.generateService();
		}
		//生成api和facade
		if(!StringUtil.isEmpty(this.packageApi)){
			//生成feign接口层
			this.generateApi();
		}
		//生成controller
		if(!StringUtil.isEmpty( this.packageController)){
			this.generateController();
		}
	}

	/**
	 * 生成controller
	 */
	private void generateController() {
		Map<String,Object> content = new HashMap<String,Object>();
		content.put("packageController", this.packageController);
		content.put("packageModel", this.packageModel);
		content.put("model", this.model);
		content.put("modelVal", StringUtil.toFirstLower(this.model));
		//当要生成api时候才继承facade
		if(!StringUtil.isEmpty(packageApi)) {
			content.put("packageFacade", this.packageFacade);
			content.put("facade", this.facade);
		}
		content.put("packageService", this.packageService);
		content.put("service", this.service);
		content.put("serviceVal", StringUtil.toFirstLower(this.model)+"Service"); 
		content.put("controller", this.controller);
		//不生成有联合主键的原子服务
		if(!this.pks.contains(",")) {
			content.put("pkAttr",GenerateHelper.getAttrName(this.pks.trim()) );
		}
		
		//生成controller
		String codeStr = FreeMarkUtil.executeUTF8("template/controller.ftl/", content);
		String fileName=this.controller+".java";
		String filePath = this.path.endsWith("/") ? 
				this.path+GenerateHelper.packageToPath(this.packageController) : 
				this.path+"/"+ GenerateHelper.packageToPath(this.packageController);
		//写入到文件
		FileUtil.writeFile(codeStr, fileName, filePath);
	}

	/**
	 * 生成api和暴露接口的facade
	 */
	private void generateApi() {
		Map<String,Object> content = new HashMap<String,Object>();
		content.put("packageApi",this.packageApi );
		content.put("packageFacade",this.packageFacade );
		content.put("facade",this.facade );
		content.put("applicationName",this.applicationName );
		content.put("api",this.api );
		content.put("packageModel",this.packageModel );
		content.put("model",this.model );
		content.put("modelVal", StringUtil.toFirstLower(this.model));
		//不生成有联合主键的原子服务
		if(!this.pks.contains(",")) {
			content.put("pkAttr",GenerateHelper.getAttrName(this.pks.trim()) );
		}
		
		//生成api
		String apiCodeStr = FreeMarkUtil.executeUTF8("template/api.ftl/", content);
		String apiFileName=this.api+".java";
		String apiFilePath = this.path.endsWith("/") ? 
				this.path+GenerateHelper.packageToPath(this.packageApi) : 
				this.path+"/"+ GenerateHelper.packageToPath(this.packageApi);
		//写入到文件
		FileUtil.writeFile(apiCodeStr, apiFileName, apiFilePath);
		
		//暴露接口的facade
		String facedeCodeStr = FreeMarkUtil.executeUTF8("template/facade.ftl/", content);
		String facedeFileName=this.facade+".java";
		String facedeFilePath = this.path.endsWith("/") ? 
				this.path+GenerateHelper.packageToPath(this.packageFacade) : 
				this.path+"/"+ GenerateHelper.packageToPath(this.packageFacade);
		//写入到文件
		FileUtil.writeFile(facedeCodeStr, facedeFileName, facedeFilePath);
	}

	/**
	 * 生成service和service实现类
	 */
	private void generateService() {
		Map<String,Object> content = new HashMap<String,Object>();
		content.put("packageService", this.packageService);
		content.put("packageModel", this.packageModel);
		content.put("model", this.model);
		content.put("modelVal", StringUtil.toFirstLower(this.model));
		content.put("service", this.service);
		content.put("packageServiceImpl", this.packageServiceImpl);
		content.put("packageMapper", this.packageMapper);
		content.put("mapper", this.mapper);
		content.put("mapperVal", StringUtil.toFirstLower(this.mapper));
		content.put("serviceImpl", this.serviceImpl);
		
		//生成service接口
		String serviceCodeStr = FreeMarkUtil.executeUTF8("template/service.ftl/", content);
		String serviceFileName=this.service+".java";
		String serviceFilePath = this.path.endsWith("/") ? 
				this.path+GenerateHelper.packageToPath(this.packageService) : 
				this.path+"/"+ GenerateHelper.packageToPath(this.packageService);
		//写入到文件
		FileUtil.writeFile(serviceCodeStr, serviceFileName, serviceFilePath);
		
		//生成service实现类
		String serviceImplCodeStr = FreeMarkUtil.executeUTF8("template/service-impl.ftl/", content);
		String serviceImplFileName=this.serviceImpl+".java";
		String serviceImplFilePath = this.path.endsWith("/") ? 
				this.path+GenerateHelper.packageToPath(this.packageServiceImpl) : 
				this.path+"/"+ GenerateHelper.packageToPath(this.packageServiceImpl);
		//写入到文件
		FileUtil.writeFile(serviceImplCodeStr, serviceImplFileName, serviceImplFilePath);
	}

	/**
	 * 生成mapper和mapper.xml
	 */
	private void generateMapper() {
		Map<String,Object> content = new HashMap<String,Object>();
		content.put("packageMapper", this.packageMapper);
		content.put("packageModel", this.packageModel);
		content.put("mapper", this.mapper);
		content.put("model", this.model);
		content.put("table", this.table);
		//mapper xml 字段映射
		List<Map<String,Object>> mappingList = new ArrayList<Map<String,Object>>();
		for(int i=0; i<this.colNames.size(); i++) {
			Map<String,Object> mapping = new HashMap<String,Object>();
			mapping.put("columnName", this.colNames.get(i)); //列名称
			mapping.put("fieldName", GenerateHelper.getAttrName(this.colNames.get(i))); //java属性名称
			mapping.put("javaType", GenerateHelper.sqlType2JavaType(this.colTypes.get(i))); //java类型
			mappingList.add(mapping);
		}
		content.put("mappingList", mappingList);
		
		//生成mapper接口
		String mapperCodeStr = FreeMarkUtil.executeUTF8("template/mapper.ftl/", content);
		String mapperFileName=this.mapper+".java";
		String mapperFilePath = this.path.endsWith("/") ? 
				this.path+GenerateHelper.packageToPath(this.packageMapper) : 
				this.path+"/"+ GenerateHelper.packageToPath(this.packageMapper);
		//写入到文件
		FileUtil.writeFile(mapperCodeStr, mapperFileName, mapperFilePath);
		
		//生成mapper xml文件
		String mapperXmlCodeStr = FreeMarkUtil.executeUTF8("template/mapper-xml.ftl/", content);
		String mapperXmlFileName=StringUtil.toFirstLower(this.model)+"-mapper.xml";
//		String mapperXmlFilePath = this.path.endsWith("/") ? 
//				this.path+ GenerateHelper.packageToPath(this.packagePre+".xml") : 
//				this.path+"/"+GenerateHelper.packageToPath(this.packagePre+".xml");
		String mapperXmlFilePath = this.path.endsWith("/") ? 	this.path+"config/mapper/":this.path+"/config/mapper/";
		if(this.subPackage!=null && this.subPackage.trim().length()!=0) {
			mapperXmlFilePath = mapperXmlFilePath + GenerateHelper.packageToPath(this.subPackage);
		}
		//写入到文件
		FileUtil.writeFile(mapperXmlCodeStr, mapperXmlFileName, mapperXmlFilePath);
	}

	/**
	 * 生成model
	 */
	private void generateModel() {
		Map<String,Object> content = new HashMap<String,Object>();
		//包路径
		content.put("packageModel", this.packageModel); 
		//表注释
		content.put("tableRemark", this.tableRemark);
		//表名称
		content.put("table", this.table);
		//类名
		content.put("model", this.model);
		//属性列表
		List<Map<String,Object>> attInfoList = new ArrayList<Map<String,Object>>();
		for (int i = 0; i < this.colNames.size(); i++) {
			Map<String,Object> attInfo = new HashMap<String,Object>();
			//属性注释
			attInfo.put("comment", this.colRemarks.get(i));
			attInfo.put("javaType", GenerateHelper.sqlType2JavaType(this.colTypes.get(i)));
			attInfo.put("fieldName", GenerateHelper.getAttrName(this.colNames.get(i)));
			attInfo.put("fieldMethod", StringUtil.toFirstUpper(GenerateHelper.getAttrName(this.colNames.get(i))));
			//注解列表组装
			List<String> annotations = new ArrayList<String>();
			if (pks.indexOf(this.colNames.get(i)) != -1) {
				//id注解
				annotations.add("@Id");
				String increFlag = this.idAutoIncrement.get(this.colNames.get(i));
				if("YES".equals(increFlag)){
					//自增属性注解
					annotations.add("@GeneratedValue(generator = \"JDBC\")");
				}
			}else {
				//一般注解
				annotations.add("@Column(name=\"" + this.colNames.get(i) +"\")");
			}
			attInfo.put("annotations",annotations);
			
			attInfoList.add(attInfo);
		}
		content.put("attInfoList", attInfoList);
		
		//生成model代码
		String codeStr = FreeMarkUtil.executeUTF8("template/model.ftl/", content);
		//写入到文件
		String fileName=this.model+".java";
		String filePath = this.path.endsWith("/") ? 
				this.path+GenerateHelper.packageToPath(this.packageModel) : 
				this.path+"/"+ GenerateHelper.packageToPath(this.packageModel);
		FileUtil.writeFile(codeStr, fileName, filePath);
	}

}
